package com.cym.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.cym.model.Location;
import com.cym.model.Param;
import com.cym.model.Server;
import com.cym.sqlhelper.bean.Page;
import com.cym.sqlhelper.bean.Sort;
import com.cym.sqlhelper.bean.Sort.Direction;
import com.cym.sqlhelper.utils.ConditionAndWrapper;
import com.cym.sqlhelper.utils.ConditionOrWrapper;
import com.cym.sqlhelper.utils.SqlHelper;
import com.cym.utils.NginxUtils;
import com.cym.utils.SnowFlakeUtils;
import com.github.odiszapc.nginxparser.NgxBlock;
import com.github.odiszapc.nginxparser.NgxConfig;
import com.github.odiszapc.nginxparser.NgxEntry;
import com.github.odiszapc.nginxparser.NgxParam;
import org.noear.solon.annotation.Inject;
import org.noear.solon.aspect.annotation.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

@Service
public class ServerService {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Inject
    SqlHelper sqlHelper;

    public Page search(Page page, String keywords) {
        ConditionOrWrapper conditionOrWrapper = new ConditionOrWrapper();
        if (StrUtil.isNotEmpty(keywords)) {
            conditionOrWrapper.like(Server::getDescr, keywords.trim())//
                    .like(Server::getServerName, keywords.trim())//
                    .like(Server::getListen, keywords.trim());

            List<String> serverIds = sqlHelper.findPropertiesByQuery(new ConditionOrWrapper() //
                            .like(Location::getDescr, keywords)//
                            .like(Location::getValue, keywords)//
                            .like(Location::getPath, keywords), //
                    Location.class, Location::getServerId);
            conditionOrWrapper.in(Server::getId, serverIds);
        }

        Sort sort = new Sort().add("seq", Direction.DESC);

        page = sqlHelper.findPage(conditionOrWrapper, sort, page, Server.class);

        return page;
    }

    public void deleteById(String id) {
        sqlHelper.deleteById(id, Server.class);
        sqlHelper.deleteByQuery(new ConditionAndWrapper().eq("serverId", id), Location.class);
    }

    public List<Location> getLocationByServerId(String serverId) {
        return sqlHelper.findListByQuery(new ConditionAndWrapper().eq("serverId", serverId), Location.class);
    }

    public void addOver(Server server, String serverParamJson, List<Location> locations) throws Exception {

        if (server.getDef() != null && server.getDef() == 1) {
            clearDef();
        }

        sqlHelper.insertOrUpdate(server);

        List<Param> paramList = new ArrayList<>();
        if (StrUtil.isNotEmpty(serverParamJson) && JSONUtil.isTypeJSON(serverParamJson)) {
            paramList = JSONUtil.toList(JSONUtil.parseArray(serverParamJson), Param.class);
        }
        List<String> locationIds = sqlHelper.findIdsByQuery(new ConditionAndWrapper().eq("serverId", server.getId()), Location.class);
        sqlHelper.deleteByQuery(new ConditionOrWrapper().eq("serverId", server.getId()).in("locationId", locationIds), Param.class);

        // 反向插入,保证列表与输入框对应
        Collections.reverse(paramList);
        for (Param param : paramList) {
            param.setServerId(server.getId());
            sqlHelper.insert(param);
        }

        List<Location> locationOlds = sqlHelper.findListByQuery(new ConditionAndWrapper().eq("serverId", server.getId()), Location.class);
        sqlHelper.deleteByQuery(new ConditionAndWrapper().eq("serverId", server.getId()), Location.class);

        if (locations != null) {
            // 反向插入,保证列表与输入框对应
            Collections.reverse(locations);

            for (Location location : locations) {
                location.setServerId(server.getId());
                location.setDescr(findLocationDescr(locationOlds, location));
                sqlHelper.insert(location);

                paramList = new ArrayList<Param>();
                if (StrUtil.isNotEmpty(location.getLocationParamJson()) && JSONUtil.isJson(location.getLocationParamJson())) {
                    paramList = JSONUtil.toList(JSONUtil.parseArray(location.getLocationParamJson()), Param.class);
                }

                // 反向插入,保证列表与输入框对应
                Collections.reverse(paramList);
                for (Param param : paramList) {
                    param.setLocationId(location.getId());
                    sqlHelper.insert(param);
                }
            }
        }
    }

    // 找到location的描述
    private String findLocationDescr(List<Location> locationOlds, Location locationNew) {

        for (Location location : locationOlds) {
            if (location.getPath().equals(locationNew.getPath()) && location.getType() == locationNew.getType()) {
                return location.getDescr();
            }

        }

        return null;
    }

    private void clearDef() {
        List<Server> servers = sqlHelper.findListByQuery(new ConditionAndWrapper().eq("def", 1), Server.class);
        for (Server server : servers) {
            server.setDef(0);
            sqlHelper.updateById(server);
        }
    }

    public void addOverTcp(Server server, String serverParamJson) {
        sqlHelper.insertOrUpdate(server);

        List<String> locationIds = sqlHelper.findIdsByQuery(new ConditionAndWrapper().eq("serverId", server.getId()), Location.class);
        sqlHelper.deleteByQuery(new ConditionOrWrapper().eq("serverId", server.getId()).in("locationId", locationIds), Param.class);
        List<Param> paramList = new ArrayList<Param>();
        if (StrUtil.isNotEmpty(serverParamJson) && JSONUtil.isTypeJSON(serverParamJson)) {
            paramList = JSONUtil.toList(JSONUtil.parseArray(serverParamJson), Param.class);
        }

        for (Param param : paramList) {
            param.setServerId(server.getId());
            sqlHelper.insert(param);
        }

        sqlHelper.deleteByQuery(new ConditionAndWrapper().eq("serverId", server.getId()), Location.class);
    }

    public List<Server> getListByProxyType(String[] proxyType) {
        Sort sort = new Sort().add("seq", Direction.DESC);
        return sqlHelper.findListByQuery(new ConditionAndWrapper().in("proxyType", proxyType), sort, Server.class);
    }

    public void importServer(String nginxPath) throws Exception {
        String initNginxPath = NginxUtils.initNginx(nginxPath);
        NgxConfig conf = NginxUtils.readByNginxPath(initNginxPath);

        List<NgxEntry> servers = conf.findAll(NgxConfig.BLOCK, "server");
        servers.addAll(conf.findAll(NgxConfig.BLOCK, "http", "server"));

        // 翻转一下,便于插入顺序和生成时一样
        Collections.reverse(servers);

        for (NgxEntry ngxEntry : servers) {
            NgxBlock serverNgx = (NgxBlock) ngxEntry;
            NgxParam serverName = serverNgx.findParam("server_name");
            Server server = new Server();
            if (serverName == null) {
                server.setServerName("");
            } else {
                server.setServerName(serverName.getValue());
            }

            server.setProxyType(0);
            NgxParam re = serverNgx.findParam("rewrite");
            if (re != null) {
                server.setRewrite(1);
            } else {
                server.setRewrite(0);
            }

            // 获取 server 洽谈配置
            StringBuilder otherServerParams = new StringBuilder();
            Iterator<NgxEntry> serverParams = serverNgx.iterator();
            Set<String> excludes = new HashSet<>(Arrays
                    .asList(
                            "server_name"
                            , "listen"
                            , "ssl_certificate"
                            , "ssl_certificate_key"
                            , "http2"
                    )
            );

            serverParams.forEachRemaining(p -> {
                if (p instanceof NgxParam) {
                    NgxParam param = (NgxParam) p;
                    if (!excludes.contains(param.getName())) {
                        if (otherServerParams.length() == 0) {
                            otherServerParams.append("[");
                        } else {
                            otherServerParams.append(",");
                        }
                        otherServerParams.append("{\"name\":\"")
                                .append(param.getName())
                                .append("\"")
                                .append(",\"value\":\"")
                                .append(param.getValue().replaceAll("\"", "\\\\\""))
                                .append("\"}");
                    }
                }
                // nginx if TODO
            });

            // 设置server
            List<NgxEntry> listens = serverNgx.findAll(NgxConfig.PARAM, "listen");
            for (NgxEntry item : listens) {
                NgxParam param = (NgxParam) item;

                if (server.getListen() == null) {
                    server.setListen((String) param.getValues().toArray()[0]);
                }

                if (param.getTokens().stream().anyMatch(item2 -> "ssl".equals(item2.getToken()))) {
                    server.setSsl(1);
                    NgxParam key = serverNgx.findParam("ssl_certificate_key");
                    NgxParam perm = serverNgx.findParam("ssl_certificate");
                    server.setKey(key == null ? "" : key.getValue());
                    server.setPem(perm == null ? "" : perm.getValue());
                }

                if (param.getTokens().stream().anyMatch(item2 -> "http2".equals(item2.getToken()))) {
                    server.setHttp2(1);
                }
            }

            // 设置location
            List<Location> locations = new ArrayList<>();
            List<NgxEntry> locationBlocks = serverNgx.findAll(NgxBlock.class, "location");
            for (NgxEntry item : locationBlocks) {
                NgxBlock block = (NgxBlock) item;
                Location location = new Location();
                // 导入的location不添加默认请求头，防止和自带的重复
                location.setHeader(0);
                location.setPath(block.getValue());
                location.setType(3);
                // 其他 location参数
                StringBuilder otherParams = new StringBuilder();
                // 解析 location
                block.iterator().forEachRemaining(p -> {
                    NgxParam param = (NgxParam) p;
                    logger.info("location {}, param {}", block.getValue(), param);
                    if ("proxy_pass".equals(param.getName())) {
                        // 动态代理 0
                        // TODO 判断是否是负载均衡upstream
                        location.setType(0);
                        location.setValue(param.getValue());
                    } else if ("root".equals(param.getName()) || "alias".equals(param.getName())) {
                        // 静态代理 1
                        location.setType(1);
                        location.setRootType(param.getName());
                        location.setRootPath(param.getValue());
                    } else {
                        // 空白代理 3
                        if ("index".equals(param.getName())) {
                            location.setRootPage(param.getValue());
                        } else {
                            if (otherParams.length() == 0) {
                                otherParams.append("[");
                            } else {
                                otherParams.append(",");
                            }
                            otherParams.append("{\"name\":\"")
                                    .append(param.getName())
                                    .append("\"")
                                    .append(",\"value\":\"")
                                    .append(param.getValue().replaceAll("\"", "\\\\\""))
                                    .append("\"}");
                        }
                    }
                });
                if (otherParams.length() != 0) {
                    otherParams.append("]");
                    System.out.println(otherParams);
                    logger.debug("location其他参数 {} : {}", location.getPath(), otherParams);
                    // 设置其他属性
                    location.setLocationParamJson(otherParams.toString());
                }
                locations.add(location);
            }

            server.setDef(0);
            server.setSeq(SnowFlakeUtils.getId());
            if (otherServerParams.length() != 0) {
                otherServerParams.append("]");
                logger.debug("server其他参数 {} : {}", server.getServerName(), otherServerParams);
                // 设置其他属性
                addOver(server, otherServerParams.toString(), locations);
            } else {
                addOver(server, "", locations);
            }
        }

        // 删除临时文件
        FileUtil.del(initNginxPath);
    }

    public void setSeq(String serverId, Integer seqAdd) {
        Server server = sqlHelper.findById(serverId, Server.class);

        List<Server> serverList = sqlHelper.findAll(new Sort("seq", Direction.DESC), Server.class);
        if (serverList.size() > 0) {
            Server tagert = null;
            if (seqAdd < 0) {
                // 下移
                for (int i = 0; i < serverList.size(); i++) {
                    if (serverList.get(i).getSeq() < server.getSeq()) {
                        tagert = serverList.get(i);
                        break;
                    }
                }
            } else {
                // 上移
                System.out.println(server.getSeq());
                for (int i = serverList.size() - 1; i >= 0; i--) {
                    System.out.println(serverList.get(i).getSeq());
                    if (serverList.get(i).getSeq() > server.getSeq()) {
                        tagert = serverList.get(i);
                        break;
                    }
                }
            }

            if (tagert != null) {

                System.err.println("tagert:" + tagert.getServerName() + tagert.getListen());
                System.err.println("server:" + server.getServerName() + server.getListen());

                // 交换seq
                Long seq = tagert.getSeq();
                tagert.setSeq(server.getSeq());
                server.setSeq(seq);

                sqlHelper.updateById(tagert);
                sqlHelper.updateById(server);
            }
        }
    }

    public void moveLocation(String locationId, Integer seqAdd) {
        Location location = sqlHelper.findById(locationId, Location.class);

        List<Location> locationList = sqlHelper.findListByQuery(new ConditionAndWrapper().eq(Location::getServerId, location.getServerId()), new Sort("id", Direction.DESC), Location.class);
        if (locationList.size() > 0) {
            Location tagert = null;
            if (seqAdd > 0) {
                // 上移
                for (int i = 0; i < locationList.size(); i++) {
                    if (Long.parseLong(locationList.get(i).getId()) < Long.parseLong(location.getId())) {
                        tagert = locationList.get(i);
                    }
                }
            } else {
                // 下移
                for (int i = locationList.size() - 1; i >= 0; i--) {
                    if (Long.parseLong(locationList.get(i).getId()) > Long.parseLong(location.getId())) {
                        tagert = locationList.get(i);
                    }
                }
            }

            if (tagert != null) {
                // 交换seq
                String id = tagert.getId();
                tagert.setId(location.getId());
                location.setId(id);

                sqlHelper.updateById(tagert);
                sqlHelper.updateById(location);
            }
        }
    }
}
